﻿/******************************************************************************

    Copyright (c) 2010, Sergey Mudrov
    All rights reserved.

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are met:
        * Redistributions of source code must retain the above copyright
          notice, this list of conditions and the following disclaimer.
        * Redistributions in binary form must reproduce the above copyright
          notice, this list of conditions and the following disclaimer in the
          documentation and/or other materials provided with the distribution.
        * Neither the name of Sergey Mudrov nor the
          names of its contributors may be used to endorse or promote products
          derived from this software without specific prior written permission.

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    DISCLAIMED. IN NO EVENT SHALL SERGEY MUDROV BE LIABLE FOR ANY
    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*******************************************************************************/

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Browser;
using System.Windows.Resources;
using System.IO;
using VkApi.DataTypes;
using System.Threading;

namespace VkApi
{
    /// <summary>
    /// Handler for API response.
    /// </summary>
    public delegate void ApiResponse_Handler(long requestID, string jsonData);
    /// <summary>
    /// Handler for settings changed event.
    /// </summary>
    public delegate void SettingsChanged_Handler(UserSettings settings);
    /// <summary>
    /// Handler for balance change event.
    /// </summary>
    public delegate void BalanceChanged_Handler(int balance);

    /// <summary>
    /// Calss that represents VK scripts.
    /// </summary>
    [ScriptableType]
    public class VkScriptable
    {
        #region Const strings.

        private const string INIT_FUNCTION_SCRIPT = @"

                function VkApi_InitVK() {
                    VK.init(function() {
                        // Add calbacks
                        VK.addCallback('onApplicationAdded', onApplicationAdded);
                        VK.addCallback('onSettingsChanged', onSettingsChanged);
                        VK.addCallback('onBalanceChanged', onBalanceChanged);
                        VK.addCallback('onWindowBlur', onWindowBlur);
                        VK.addCallback('onWindowFocus', onWindowFocus);

                        // API initialization succeed
                        element = document.getElementById('{0}');
                        element.content.VkFacade.OnInitOk();

                    }, function() {
                        // API initialization failed
                        element = document.getElementById('{0}');
                        element.content.VkFacade.OnInitFailed();
                    });
                }

                function translator(num) {
                    return function(data) {
                        callback(num, data);
                    };
                }

                function callback(num, data) {
                    element = document.getElementById('{0}');
                    element.content.VkFacade.OnApiResponse(num, JSON.stringify(data));
                }

                function onApplicationAdded() {
                    element = document.getElementById('{0}');
                    element.content.VkFacade.OnApplicationAdded();
                }

                function onSettingsChanged(settings) {
                    element = document.getElementById('{0}');
                    element.content.VkFacade.OnSettingsChanged(settings);
                }

                function onBalanceChanged(balance) {
                    element = document.getElementById('{0}');
                    element.content.VkFacade.OnBalanceChanged(balance);
                }

                function onWindowBlur() {
                    element = document.getElementById('{0}');
                    element.content.VkFacade.OnWindowBlur();
                }

                function onWindowFocus() {
                    element = document.getElementById('{0}');
                    element.content.VkFacade.OnWindowFocus();
                }

                ";

        #endregion

        private string _objectID = null;

        internal event EventHandler ApplicationAdded;
        internal event SettingsChanged_Handler SettingsChanged;
        internal event BalanceChanged_Handler BalanceChanged;
        internal event EventHandler WindowBlur;
        internal event EventHandler WindowFocus;

        internal event EventHandler InitOk;
        internal event EventHandler InitFailed;
        internal event ApiResponse_Handler ApiResponse;
        
        /// <summary>
        /// 
        /// </summary>
        internal VkScriptable(string objectID)
        {
            this._objectID = objectID;

#if DEBUG
            Trace.WriteLine("Register VKFacade"); 
#endif

            HtmlPage.RegisterScriptableObject("VkFacade", this);

            this.InsertScripts();
        }

        #region Script callbacks.

        /// <summary>
        /// Called when initialization is ok.
        /// </summary>
        [ScriptableMember]
        public void OnInitOk()
        {
            if (this.InitOk != null)
                this.InitOk(this, EventArgs.Empty);
        }

        /// <summary>
        /// Called when initialization failed.
        /// </summary>
        [ScriptableMember]
        public void OnInitFailed()
        {
            if (this.InitFailed != null)
                this.InitFailed(this, EventArgs.Empty);
        }

        /// <summary>
        /// Called when API responses.
        /// </summary>
        /// <param name="requestID">The request ID.</param>
        /// <param name="jsonData">The json data.</param>
        [ScriptableMember]
        public void OnApiResponse(long requestID, string jsonData)
        {
            if (this.ApiResponse != null)
                this.ApiResponse(requestID, jsonData);
        }

        /// <summary>
        /// Called when application is added.
        /// </summary>
        [ScriptableMember]
        public void OnApplicationAdded()
        {
            if (this.ApplicationAdded != null)
                this.ApplicationAdded(this, EventArgs.Empty);
        }

        /// <summary>
        /// Called when settings are changed.
        /// </summary>
        /// <param name="settings">The settings.</param>
        [ScriptableMember]
        public void OnSettingsChanged(int settings)
        {
            if (this.SettingsChanged != null)
                this.SettingsChanged((UserSettings)settings);
        }

        /// <summary>
        /// Called when balance is changed.
        /// </summary>
        /// <param name="balance">The balance.</param>
        [ScriptableMember]
        public void OnBalanceChanged(int balance)
        {
            if (this.BalanceChanged != null)
                this.BalanceChanged(balance);
        }

        /// <summary>
        /// Called when window is blured.
        /// </summary>
        [ScriptableMember]
        public void OnWindowBlur()
        {
            if (this.WindowBlur != null)
                this.WindowBlur(this, EventArgs.Empty);
        }

        /// <summary>
        /// Called when on window focus.
        /// </summary>
        [ScriptableMember]
        public void OnWindowFocus()
        {
            if (this.WindowFocus != null)
                this.WindowFocus(this, EventArgs.Empty);
        } 

        #endregion               

        /// <summary>
        /// 
        /// </summary>
        private void InsertScripts()
        {
            try
            {
#if DEBUG
                Trace.WriteLine("--> VkScriptable: InsertScripts - start"); 
#endif

                HtmlElement element = null;

                // Add JSON script
#if DEBUG
                Trace.WriteLine("VkScriptable: Add JSON handler script - start"); 
#endif

                StreamResourceInfo sri = Application.GetResourceStream(new Uri("VkApi;component/Scripts/jsorg.js", UriKind.Relative));
                StreamReader s = new StreamReader(sri.Stream);

                element = HtmlPage.Document.CreateElement("script");
                element.SetProperty("type", "text/javascript");
                element.SetProperty("text", s.ReadToEnd());
                HtmlPage.Document.DocumentElement.AppendChild(element);

#if DEBUG
                Trace.WriteLine("VkScriptable: Add JSON handler script - end"); 
#endif

                // Add init script
#if DEBUG
                Trace.WriteLine("VkScriptable: Add VK init script - start"); 
#endif
                sri = Application.GetResourceStream(new Uri("VkApi;component/Scripts/ScriptableFuncs.js", UriKind.Relative));
                s = new StreamReader(sri.Stream);

                element = HtmlPage.Document.CreateElement("script");
                element.SetProperty("type", "text/javascript");
                element.SetProperty("text", s.ReadToEnd().Replace("{0}", this._objectID));
                HtmlPage.Document.DocumentElement.AppendChild(element);

#if DEBUG
                Trace.WriteLine("VkScriptable: Add VK init script - end");

                Trace.WriteLine("--> VkScriptable: InsertScripts - end"); 
#endif
            }
            catch (Exception ex)
            {
                throw new Exception("Failed to create init script!", ex);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        internal void InsertBackupVK()
        {
            StreamResourceInfo sri = Application.GetResourceStream(new Uri("VkApi;component/Scripts/VkBackup.js", UriKind.Relative));
            StreamReader s = new StreamReader(sri.Stream);

            HtmlElement element = HtmlPage.Document.CreateElement("script");
            element.SetProperty("type", "text/javascript");
            element.SetProperty("text", s.ReadToEnd());
            HtmlPage.Document.DocumentElement.AppendChild(element);
        }
    }
}
